package org.bankabeule.ui.utils;

import java.util.ArrayList;
import java.util.List;

import org.bankabeule.Date;
import org.bankabeule.Operation;
import org.bankabeule.Tag;
import org.bankabeule.ui.actions.RemoveAction;
import org.bankabeule.util.Providers;
import org.bankabeule.util.Utils;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.IWorkbenchActionConstants;

/**
 * @author sdudoit
 */
public class OperationsTableViewer extends TableViewer {

	/**
	 * Listeners
	 */
	private final List<OperationsTableListener> listeners = new ArrayList<OperationsTableListener>();

	/**
	 * Constructor
	 * 
	 * @param parent
	 */
	public OperationsTableViewer(Composite parent) {
		super(parent, SWT.MULTI | SWT.H_SCROLL
				| SWT.V_SCROLL | SWT.FULL_SELECTION);

		Table table = this.getTable();
		table.setHeaderVisible(true);
		table.setLinesVisible(true);

		TableColumnLayout ad = new TableColumnLayout();
		parent.setLayout(ad);

		TableColumn idColumn = new TableColumn(table, SWT.CENTER);
		idColumn.setText("");
		ad.setColumnData(idColumn, new ColumnWeightData(3, 5));

		TableColumn dateColumn = new TableColumn(table, SWT.CENTER);
		dateColumn.setText("Date");
		ad.setColumnData(dateColumn, new ColumnWeightData(12, 10));
		new ColumnViewerSorter(this, dateColumn) {
			@Override
			protected int doCompare(Viewer viewer, Object e1, Object e2) {
				Date d1 = ((Operation) e1).getDate();
				Date d2 = ((Operation) e2).getDate();
				return Utils.compare(d1, d2);
			}
		};

		TableColumn nameColumn = new TableColumn(table, SWT.LEFT);
		nameColumn.setText("Name");
		ad.setColumnData(nameColumn, new ColumnWeightData(35, 100));
		new ColumnViewerSorter(this, nameColumn) {
			@Override
			protected int doCompare(Viewer viewer, Object e1, Object e2) {
				String n1 = ((Operation) e1).getName();
				String n2 = ((Operation) e2).getName();
				return n1.compareTo(n2);
			}
		};

		TableColumn debitColumn = new TableColumn(table, SWT.RIGHT);
		debitColumn.setText("Debit");
		ad.setColumnData(debitColumn, new ColumnWeightData(15, 15));
		new ColumnViewerSorter(this, debitColumn) {
			@Override
			protected int doCompare(Viewer viewer, Object e1, Object e2) {
				double v1 = ((Operation) e1).getValue();
				double v2 = ((Operation) e2).getValue();
				return (int) (v1 - v2);
			}
		};

		TableColumn creditColumn = new TableColumn(table, SWT.RIGHT);
		creditColumn.setText("Credit");
		ad.setColumnData(creditColumn, new ColumnWeightData(15, 15));
		new ColumnViewerSorter(this, creditColumn) {
			@Override
			protected int doCompare(Viewer viewer, Object e1, Object e2) {
				double v1 = ((Operation) e1).getValue();
				double v2 = ((Operation) e2).getValue();
				return (int) (v1 - v2);
			}
		};

		TableColumn tagsColumn = new TableColumn(table, SWT.LEFT);
		tagsColumn.setText("Tags");
		ad.setColumnData(tagsColumn, new ColumnWeightData(20, 50));

		this.setContentProvider(Providers.getContentProvider());
		this.setLabelProvider(Providers.getLabelProvider());
		this.setComparator(new ViewerComparator() {

			@Override
			public int compare(Viewer viewer, Object e1, Object e2) {
				Date d1 = ((Operation) e1).getDate();
				Date d2 = ((Operation) e2).getDate();
				return Utils.compare(d1, d2);
			}

		});

		final Viewer viewer = this;

		MenuManager contextMenu = new MenuManager("#PopUp"); //$NON-NLS-1$
		contextMenu.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				manager.removeAll();
				manager.add(new RemoveAction(viewer));
				manager.add(new Separator(
						IWorkbenchActionConstants.MB_ADDITIONS));

			}

		});
		Menu menu = contextMenu.createContextMenu(table);
		table.setMenu(menu);

		// add drop support
		this.addDragSupport(DND.DROP_MOVE,
				new Transfer[] { LocalSelectionTransfer.getTransfer() },
				new DragSourceAdapter() {
			@Override
			public void dragStart(DragSourceEvent event) {
				LocalSelectionTransfer.getTransfer().setSelection(
						OperationsTableViewer.this.getSelection());
				event.doit = true;
			}

			@Override
			public void dragSetData(DragSourceEvent event) {
				if (LocalSelectionTransfer.getTransfer()
						.isSupportedType(event.dataType)) {
					event.data = OperationsTableViewer.this
					.getSelection();
				}
			}

			@Override
			public void dragFinished(DragSourceEvent event) {
				LocalSelectionTransfer.getTransfer().setSelection(null);
				OperationsTableViewer.this.refresh();
			}
		});
		this.addDropSupport(DND.DROP_MOVE,
				new Transfer[] { LocalSelectionTransfer.getTransfer() },
				new DropTargetAdapter() {
			@Override
			public void drop(DropTargetEvent event) {
				if (LocalSelectionTransfer.getTransfer()
						.isSupportedType(event.currentDataType)) {
					StructuredSelection selection = (StructuredSelection) event.data;
					Operation operation = (Operation) ((TableItem) event.item)
					.getData();
					for (Object object : selection.toList()) {
						operation.getTags().add((Tag) object);
					}
					OperationsTableViewer.this.refresh();
				}
			}
		});
	}

	/**
	 * 
	 */
	@Override
	public void refresh() {
		super.refresh();
		for (OperationsTableListener listener : this.listeners) {
			listener.refreshed();
		}
	}

	/**
	 * 
	 */
	public void addOperationsTableListener(OperationsTableListener listener) {
		this.listeners.add(listener);
	}

	/**
	 * 
	 */
	public void removeOperationsTableListener(OperationsTableListener listener) {
		this.listeners.remove(listener);
	}


	/**
	 * @author sdudoit
	 */
	private static abstract class ColumnViewerSorter extends ViewerComparator {

		public static final int ASC = 1;

		public static final int NONE = 0;

		public static final int DESC = -1;

		private int direction = 0;

		private final TableColumn column;

		private final ColumnViewer viewer;

		public ColumnViewerSorter(ColumnViewer viewer, TableColumn column) {
			this.column = column;
			this.viewer = viewer;
			this.column.addSelectionListener(new SelectionAdapter() {

				@Override
				public void widgetSelected(SelectionEvent e) {
					if( ColumnViewerSorter.this.viewer.getComparator() != null ) {
						if( ColumnViewerSorter.this.viewer.getComparator() == ColumnViewerSorter.this ) {
							int tdirection = ColumnViewerSorter.this.direction;

							if( tdirection == ColumnViewerSorter.ASC ) {
								ColumnViewerSorter.this.setSorter(ColumnViewerSorter.this, ColumnViewerSorter.DESC);
							} else if( tdirection == ColumnViewerSorter.DESC ) {
								ColumnViewerSorter.this.setSorter(ColumnViewerSorter.this, ColumnViewerSorter.NONE);
							}
						} else {
							ColumnViewerSorter.this.setSorter(ColumnViewerSorter.this, ColumnViewerSorter.ASC);
						}
					} else {
						ColumnViewerSorter.this.setSorter(ColumnViewerSorter.this, ColumnViewerSorter.ASC);
					}
				}
			});
		}

		public void setSorter(ColumnViewerSorter sorter, int direction) {
			if( direction == ColumnViewerSorter.NONE ) {
				this.column.getParent().setSortColumn(null);
				this.column.getParent().setSortDirection(SWT.NONE);
				this.viewer.setComparator(null);
			} else {
				this.column.getParent().setSortColumn(this.column);
				sorter.direction = direction;

				if( direction == ColumnViewerSorter.ASC ) {
					this.column.getParent().setSortDirection(SWT.DOWN);
				} else {
					this.column.getParent().setSortDirection(SWT.UP);
				}

				if( this.viewer.getComparator() == sorter ) {
					this.viewer.refresh();
				} else {
					this.viewer.setComparator(sorter);
				}

			}
		}

		@Override
		public int compare(Viewer viewer, Object e1, Object e2) {
			return this.direction * this.doCompare(viewer, e1, e2);
		}

		protected abstract int doCompare(Viewer viewer, Object e1, Object e2);
	}



}
